In [ ]:
class Student(object):
"""
The above states that the code-block (indented area) below will define a
class Student, that derives from a class called 'object'. Inheriting from 'object' is S
"""
def __init__(self, name, birthyear, interest=None):
"""__init__ is special method that is called when instantiating the object.
Typically the methods can then be used to """
self.name = name
self.birthyear = birthyear
self.interest = interest
def say_hi(self):
""" This is a classical example of a function that prints something.
The more complex your system, the less likely it is that it is a good idea to print anything other than
warnings from whithin your classes."""
if not self.interest:
print("Hi, my name is " + self.name + "!")
else:
print("Hi, my name is " + self.name + " and I'm interested in " + self.interest + ".")
def get_age(self):
""" This is a much more style-pure example of classes.
Recording a birthyear instead of age is a good idea because next year we'll all be a year older.
However requiring everyone who uses your class is impolite and would lead to duplicate code.
Doing it once and asking everyone to use that implementation reduces code complexity and improves
maintainability.
"""
import datetime
return datetime.datetime.now().year-self.birthyear
The above construct is a class, which is to say a model for creating objects.
To create an object we say we instantiate a class.
In [ ]:
jyry = Student("Jyry", 1984, interest="Python")
Now we have an object called "jyry", which has the value s listed above. We can call methods of the object and access the variables associated with the object.
In [ ]:
jyry.say_hi()
In [ ]:
print(jyry.birthyear)
One can create multiple objects that all have their own identity, even though they share some variables.
In [ ]:
tuomas = Student("Tuomas", 1984, interest="Java")
tuomas.say_hi()
Typically object comparison is done using the same syntax as for basic types (which, by the way are objects too in Python).
If you want to implement special logic for comparisons in your own classes, look up magic methods either online or in another part of this introduction. It is a very common task and helps people who use your code (i.e. you).
In [ ]:
tuomas == jyry
Python permits the programmer to edit objects without any access control mechanics. See for example.
In [ ]:
jyry.interest = "teaching"
jyry.say_hi()
In [ ]:
fobj = open("../data/grep.txt")
How can we find things out about this object? Below are a few examples:
__str__()-method of the object, which should return a (more or less) human-readable definition of the objectdir() lists the attributes of an object, that is to say functions and variables associated with ithelp-function attempts to find the docstring for your function__doc__ attribute of object members contains the docstring if available to the interpreterThis list is not comprehensive.
In [ ]:
print(fobj)
In [ ]:
dir(fobj)
In [ ]:
help(jyry.say_hi)
In [ ]:
jyry.say_hi.__doc__
In Python, exceptions are lightweight, i.e. handling them doesn't cause a notable decrease in performance as happens in some languages.
The purpose of exceptions is to communicate that something didn't go right. The name of the exception typically tells what kind of error ocurred and the exception can also contain a more explicit message.
In [ ]:
class Container(object):
def __init__(self):
self.bag = {}
def put(self, key, item):
self.bag[key] = item
def get(self, key):
return self.bag[key]
The container-class can exhibit at least two different exceptions.
In [ ]:
container = Container()
container.put([1, 2, 3], "example")
In [ ]:
container.get("not_in_it")
Who should worry about the various issues is a good philosophical question. We could either make the Container-class secure in that it doesn't raise any errors to whoever calls it or we could let the caller worry about such errors.
For now let's assume that the programmer is competent and knows what is a valid key and what isn't.
In [ ]:
try:
container = Container()
container.put([1,2,3], "value")
except TypeError as err:
print("Stupid programmer caused an error: " + str(err))
A try-except may contain a finallyblock, which is always guaranteed to execute.
Also, it is permissible to catch multiple different errors.
In [ ]:
try:
container = Container()
container.put(3, "value")
container.get(3)
except TypeError as err:
print("Stupid programmer caused an error: " + str(err))
except KeyError as err:
print("Stupid programmer caused another error: " + str(err))
finally:
print("all is well in the end")
# go ahead, make changes that cause one of the exceptions to be raised
There is also syntax for catching multiple error types in the same catch clause.
The keyword raise is used to continue error handling. This is useful if you want to log errors but let them pass onward anyway.
A raise without arguments will re-raise the error that was being handled.
In [ ]:
try:
container = Container()
container.put(3, "value")
container.get(5)
except (TypeError, KeyError) as err:
print("please shoot me")
if type(err) == TypeError:
raise Exception("That's it I quit!")
else:
raise